package com.qiongqi.datadir.utils.security;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;

import java.io.IOException;
import java.io.StringWriter;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import static org.apache.commons.lang3.StringUtils.containsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

/**
 * 代理重加密
 */
public class PREUtils {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    // ================= 密钥管理 =================
    // Application->generate KeyPair->generate CSR->POST CSR in PEM format -> CA
    // CA -> sign CSR -> send back signature in PEM format -> Application
    /**
     * 生成RSA密钥对
     * @param keySize
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     */
    public static KeyPair generateRSAKeyPair(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", "BC");
        gen.initialize(keySize);
        return gen.generateKeyPair();
    }

    /**
     * 生成椭圆曲线密钥对（更高效）
     * @param curveName
     * @return
     * @throws Exception
     */
    public static KeyPair generateECKeyPair(String curveName) throws Exception {
        KeyPairGenerator gen = KeyPairGenerator.getInstance("EC", "BC");
        gen.initialize(new ECGenParameterSpec(curveName));
        return gen.generateKeyPair();
    }

    /**
     * 生成CSR(证书签名请求)
     * @param keyPair 密钥对
     * @param subject 主题信息(格式: "CN=commonName, O=organization, C=country")
     * @return PEM格式的CSR字符串
     */
    public static String generateCSR(KeyPair keyPair, String subject) throws Exception {
        X500Name subjectName = new X500Name(subject);
        ContentSigner signer = new JcaContentSignerBuilder("SHA256withECDSA")
                .setProvider("BC") // 指定 BC 提供者
                .build(keyPair.getPrivate());
        PKCS10CertificationRequest csr = new JcaPKCS10CertificationRequestBuilder(subjectName, keyPair.getPublic())
                .build(signer);
//        PKCS10CertificationRequest csr = new JcaPKCS10CertificationRequestBuilder(subjectName, keyPair.getPublic())
//                                  .build(new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate()));
        return convertToPEM(csr.getEncoded(), "CERTIFICATE REQUEST");
    }

    /**
     * 将私钥转换为PEM格式
     * @param privateKey 私钥对象
     * @return PEM格式的私钥字符串
     */
    public static String getPrivateKeyPEM(PrivateKey privateKey) throws Exception {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
        return convertToPEM(spec.getEncoded(), "PRIVATE KEY");
    }

    /**
     * 将公钥转换为PEM格式
     * @param publicKey 公钥对象
     * @return PEM格式的公钥字符串
     */
    public static String getPublicKeyPEM(PublicKey publicKey) throws Exception {
        return convertToPEM(publicKey.getEncoded(), "PUBLIC KEY");
    }

    /**
     * 将字节数组转换为PEM格式
     * @param content 二进制内容
     * @param type PEM对象类型
     * @return PEM格式字符串
     */
    private static String convertToPEM(byte[] content, String type) throws IOException {
        PemObject pemObject = new PemObject(type, content);
        StringWriter stringWriter = new StringWriter();
        try (PemWriter pemWriter = new PemWriter(stringWriter)) {
            pemWriter.writeObject(pemObject);
        }
        return stringWriter.toString();
    }

    // ================= 代理重加密核心 =================
    /**
     * 生成重加密密钥（基于椭圆曲线方案）
     * @param alicePrivate 委托方私钥
     * @param bobPublic    接收方公钥
     * @param condition    访问条件（如时间戳/IP）
     * @return 重加密密钥字节数组
     */
    public static byte[] generateReKey(PrivateKey alicePrivate, PublicKey bobPublic, String condition) throws Exception {
        // 1. 转换椭圆曲线参数
        ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
        BigInteger s = new BigInteger(1, alicePrivate.getEncoded()); // Alice私钥
        ECPoint Q = ecSpec.getCurve().decodePoint(bobPublic.getEncoded()); // Bob公钥

        // 2. 绑定访问条件（生产环境应使用HMAC）
        byte[] conditionHash = MessageDigest.getInstance("SHA-256")
                .digest(condition.getBytes());

        // 3. 计算重加密密钥: rk = s^{-1} * Q + H(condition)*G
        BigInteger sInverse = s.modInverse(ecSpec.getN());
        ECPoint rkPoint = Q.multiply(sInverse)
                .add(ecSpec.getG().multiply(new BigInteger(1, conditionHash)));

        return rkPoint.getEncoded(true); // 压缩格式
    }

    /**
     * 执行代理重加密
     * @param originalCiphertext 原始密文
     * @param reKey              重加密密钥
     * @param condition          访问条件
     * @return 重加密后的密文
     */
    public static byte[] reEncrypt(byte[] originalCiphertext, byte[] reKey, String condition) throws Exception {
        // 1. 验证访问条件有效性（示例：实际需校验时间/签名等）
        if (!isValidCondition(condition)) {
            throw new SecurityException("Invalid access condition");
        }

        // 2. 转换重加密密钥为椭圆曲线点
        ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
        ECPoint rkPoint = ecSpec.getCurve().decodePoint(reKey);

        // 3. 计算新密文: C' = rk * C
        ECPoint originalPoint = ecSpec.getCurve().decodePoint(originalCiphertext);
        ECPoint newCipherPoint = originalPoint.multiply(rkPoint.getXCoord().toBigInteger());

        return newCipherPoint.getEncoded(true);
    }

    /**
     * 解密重加密后的数据
     * @param reEncryptedCiphertext 重加密密文
     * @param bobPrivateKey         接收方私钥
     * @return 解密后的明文
     */
    public static byte[] decrypt(byte[] reEncryptedCiphertext, PrivateKey bobPrivateKey) throws Exception {
        // 使用安全的密钥转换方法
        AsymmetricKeyParameter privKeyParam = convertToPrivateKeyParameter(bobPrivateKey);

        if (privKeyParam instanceof RSAKeyParameters) {
            RSAEngine engine = new RSAEngine();
            engine.init(false, privKeyParam);
            return engine.processBlock(reEncryptedCiphertext, 0, reEncryptedCiphertext.length);
        } else {
            throw new UnsupportedOperationException("Only RSA/EC decryption supported");
        }
    }

    // ================= 辅助方法 =================
    private static AsymmetricKeyParameter convertToPrivateKeyParameter(PrivateKey privateKey) throws Exception {
        return PrivateKeyFactory.createKey(privateKey.getEncoded());
    }

    private static boolean isValidCondition(String condition) {
        // 示例：实际应验证时间戳/签名/白名单等
        return isNotBlank(condition) && !containsIgnoreCase(condition, "expired");
    }

    // 密钥序列化工具
    public static String serializeKey(Key key) {
        return Base64.getEncoder().encodeToString(key.getEncoded());
    }

    public static PublicKey deserializePublicKey(String keyStr, String algorithm) throws Exception {
        byte[] data = Base64.getDecoder().decode(keyStr);
        KeyFactory kf = KeyFactory.getInstance(algorithm, "BC");
        return kf.generatePublic(new X509EncodedKeySpec(data));
    }

    public static PrivateKey deserializePrivateKey(String keyStr, String algorithm) throws Exception {
        byte[] data = Base64.getDecoder().decode(keyStr);
        KeyFactory kf = KeyFactory.getInstance(algorithm, "BC");
        return kf.generatePrivate(new PKCS8EncodedKeySpec(data));
    }
}
